<?php
if (!defined('IN_ECM')) {
	exit ('403 Forbidden');
}

/* 模型相关常量定义 */
define('HAS_ONE', 1); //一对一关联
define('BELONGS_TO', 2); //属于关联
define('HAS_MANY', 3); //一对多关联
define('HAS_AND_BELONGS_TO_MANY', 4); //多对多关联
define('DROP_CONDITION_TRUNCATE', 'TRUNCATE'); //清空

/*
除本基类文件外，所有的模型类的类名的构造规则应该是模型名(首字母大写)+model组成，文件名必须是模型名+.model组成
如有一个用户模型，模型名为user，则其文件名应为user.model.php，类名为UserModel
*/
class BaseModel extends Object {
	var $db = null;

	/* 所映射的数据库表 */
	var $table = '';

	/* 主键 */
	var $prikey = '';

	/* 别名 */
	var $alias = '';

	/* 模型的名称 */
	var $_name = '';

	/* 表前缀 */
	var $_prefix = '';

	/* 数据验证规则 */
	var $_autov = array ();

	/* 查询统计 */
	var $_last_query_count = -1;

	/* 临时保存已删除的数据 */
	var $_dropped_data = array ();

	/* 关系(定义关系时，只有belongs_to以及has_and_belongs_to_many需要指定reverse反向关系) */
	var $_relation = array ();

	function __construct($params, $db) { //echo "<br/>".$params."  12<br/>";
		$this->BaseModel($params, $db);
	}
	/**
	 *  构造函数
	 *
	 *  @author Garbin
	 *  @param  array  $params
	 *  @param  object $db
	 *  @return void
	 */
	function BaseModel($params, $db) { //echo "456";
		//die();
		$this->db = & $db;
		!$this->alias && $this->alias = $this->table;
		$this->_prefix = DB_PREFIX;
		$this->table = $this->_prefix . $this->table;
		if (!empty ($params)) {
			foreach ($params as $key => $value) {
				$this-> $key = $value;
			}
		}
	}

	/**
	 *    获取模型名称
	 *
	 *    @author    Garbin
	 *    @return    void
	 */
	function getName() {
		return $this->_name;
	}

	/**
	 *    获取单一一条记录
	 *
	 *    @author    Garbin
	 *    @param     mixed $params
	 *    @return    array
	 */
	function get($params) {
		$data = $this->find($params);
		if (!is_array($data)) {
			return array ();
		}

		return current($data);
	}

	/**
	 * 根据id取得信息
	 *
	 * @param mix $id
	 * @return array
	 */
	function get_info($id) {
		$rows = $this->find(array (
			'conditions' => intval($id
		),));
		return $rows ? current($rows) : array ();
	}

	/**
	 *  根据一定条件找出相关数据(不连接其它模型，直接通过JOIN语句来查询)
	 *
	 *  @author Garbin
	 *  @param  mixed  $params
	 *  @return array
	 */
	function find($params = array ()) {
		extract($this->_initFindParams($params));

		/* 字段(SELECT FROM) */
		$fields = $this->getRealFields($fields);
		$fields == '' && $fields = '*';

		$tables = $this->table . ' ' . $this->alias;
		/* 左联结(LEFT JOIN) */
		$join_result = $this->_joinModel($tables, $join);

		/* 原来为($join_result || $index_key)，忘了最初的用意，默认加上主键应该是只为了为获得索引的数组服务的，因此只跟索引键是否是主键有关 */
		if ($index_key == $this->prikey || (is_array($index_key) && in_array($this->prikey, $index_key))) {
			/* 如果索引键里有主键，则默认在要查询字段后加上主键 */
			$fields .= ",{$this->alias}.{$this->prikey}";
		}

		/* 条件(WHERE) */
		$conditions = $this->_getConditions($conditions, true);

		/* 排序(ORDER BY) */
		$order && $order = ' ORDER BY ' . $this->getRealFields($order);

		/* 分页(LIMIT) */
		$limit && $limit = ' LIMIT ' . $limit;
		if ($count) {
			$this->_updateLastQueryCount("SELECT COUNT(*) as c FROM {$tables}{$conditions}");
		}

		/* 完整的SQL */
		$sql = "SELECT {$fields} FROM {$tables}{$conditions}{$order}{$limit}";
		return $index_key ? $this->db->getAllWithIndex($sql, $index_key) : $this->db->getAll($sql);
	}

	function findstore($params = array (), $array = array ()) { //查询店铺
		/*条件(conditions)*/
		if(!empty($params['conditions'])){
		   $conditions=$params['conditions'];
		}
		/* 分页(LIMIT) */
		$limit = ' LIMIT ' . $params['limit'];
		if (!empty ($array[0])) { //店铺分类
			$catesql = " and category_store.cate_id = $array[0]";
			$catecountsql = " and category_store.cate_id = $array[0]";
		}
		if (!empty ($array[1])) { //地区分类
			$regionsql = " and region_id = $array[1]";
			$regioncountsql = " and region_id = $array[1]";
		}
		if (!empty ($array[2])) { //关键字查询
			$keywordsql = " and s.store_name like '%" . $array[2] . "%' ";
		}
		$sqlcount = "select count(*) from " .
		"(SELECT s.store_id FROM ecm_store s LEFT JOIN ecm_member member  " .
		"ON s.store_id = member.user_id LEFT JOIN ecm_category_store category_store  " .
		"ON s.store_id = category_store.store_id  WHERE state = 1 $catecountsql $regioncountsql $keywordsql $conditions) a";
		$this->_updateLastQueryCount($sqlcount);

		$sql = "SELECT *,s.store_id FROM ecm_store s " .
		"LEFT JOIN ecm_member member ON s.store_id = member.user_id " .
		"LEFT JOIN ecm_category_store category_store " .
		"ON s.store_id = category_store.store_id WHERE state = 1 " . $catesql . $regionsql . $keywordsql . $conditions ." ORDER BY add_time desc " . $limit;
		return $this->db->getAll($sql);
	}

	function findprice($price = "") {
		if (empty ($price)) {
			$sql = "select price_id,price_name,tag from ecm_price";
		} else {
			$sql = "select price_id,price_name,tag from ecm_price where price_id=$price";
		}
		return $this->db->getAll($sql);
	}

	function findbrand() {
		$sql = "select * from ecm_brand";
		return $this->db->getAll($sql);
	}

	function get_daototal($param = array ()) {
		$sql = "select count(*) from (SELECT g.goods_id, g.store_id, g.type, g.goods_name, g.cate_id, g.cate_name, g.brand, g.spec_qty, g.spec_name_1, g.spec_name_2,
								g.if_show, g.closed, g.add_time, g.recommended, g.default_image, gs.spec_id, gs.spec_1, gs.spec_2, gs.color_rgb, gs.price,
								gs.stock, s.store_name, s.region_id, s.region_name, s.credit_value, s.sgrade, gst.views, gst.sales, gst.comments
								FROM ecm_goods g LEFT JOIN ecm_goods_spec gs ON g.default_spec = gs.spec_id LEFT JOIN ecm_store s
								ON g.store_id = s.store_id LEFT JOIN ecm_goods_statistics gst ON g.goods_id = gst.goods_id
								WHERE g.if_show = 1 AND g.closed = 0 AND s.state = 1 AND gs.spec_id IS NOT NULL AND s.store_id IS NOT NULL";
		if (count($param) == 0) {
			$sql = $sql . ")  a";
		} else
			if ($param['hottype'] != "" && $param['hottype'] != null && $param['hottype'] == "hot") { //热门搜索
				//            $sql="select count(*) from (select goods_id from ecm_goods where store_id in " .
				//            	 "(select store_id from ecm_category_store where cate_id in " .
				//            	 "(select cate_id from ecm_scategory where cate_name = '".$param['keyword'][0]."')) " .
				//            	 "and  if_show = 1 AND closed = 0) a";
				$sql = $sql . " and g.goods_name like '%" . $param['keyword'][0] . "%' " . ")  a";
			} else {
				$keyvar;
				$keyvalue;
				foreach ($param as $key => $value) {
					$keyvar = $key;
					$keyvalue = $value;
				}
				if ($keyvar == "brand") {
					$brandsql = " AND g.brand = '" . $value . "' ";
					$sql = $sql . $brandsql . ")  a";
				}else{
						list ($pr1, $pr2) = explode("-", $param['keyword'][0]);
						if (!empty ($param['numtype'])) {
							$sqllike = " and g.goods_name like '%" . $pr1 . "%' ";
							$sql = $sql . $sqllike . ")  a";
						} else {
							if (preg_match("/[^\d-., ]/", $pr1)) { //不是数字
								$sqllike = " and g.goods_name like '%" . $pr1 . "%' ";
								$sql = $sql . $sqllike . ")  a";
							} else {
								if (empty ($pr2) && !empty ($pr1)) { //最大价格
									$sqlpr2 = " and g.price > $pr1 ";
									$sql = $sql . $sqlpr2 . ")  a";
								} else {
									$sqlpr1pr2 = " and g.price < $pr2 and g.price > $pr1 ";
									$sql = $sql . $sqlpr1pr2 . ")  a";
								}
							}
						}
				}
			}
		$total = $this->db->getTotal($sql);
		return $total;
	}

    function get_catedaototal($param = array ()) {
    	$sql = "select count(*) from (SELECT g.goods_id, g.store_id, g.type, g.goods_name, g.cate_id, g.cate_name, g.brand, g.spec_qty, g.spec_name_1, g.spec_name_2,
								g.if_show, g.closed, g.add_time, g.recommended, g.default_image, gs.spec_id, gs.spec_1, gs.spec_2, gs.color_rgb, gs.price,
								gs.stock, s.store_name, s.region_id, s.region_name, s.credit_value, s.sgrade, gst.views, gst.sales, gst.comments
								FROM ecm_goods g LEFT JOIN ecm_goods_spec gs ON g.default_spec = gs.spec_id LEFT JOIN ecm_store s
								ON g.store_id = s.store_id LEFT JOIN ecm_goods_statistics gst ON g.goods_id = gst.goods_id
								WHERE g.if_show = 1 AND g.closed = 0 AND  g.cate_id = '".$param['cate_id']."' AND " .
										"s.state = 1 AND gs.spec_id IS NOT NULL AND s.store_id IS NOT NULL ) a ";
        $total = $this->db->getTotal($sql);
		return $total;
    }

	function get_goods_daototal($array = array ()) {
		$sql = "select count(*) from (SELECT g.goods_id, g.store_id, g.type, g.goods_name, g.cate_id, g.cate_name, g.brand, g.spec_qty, g.spec_name_1, g.spec_name_2,
								g.if_show, g.closed, g.add_time, g.recommended, g.default_image, gs.spec_id, gs.spec_1, gs.spec_2, gs.color_rgb, gs.price,
								gs.stock, s.store_name, s.region_id, s.region_name, s.credit_value, s.sgrade, gst.views, gst.sales, gst.comments
								FROM ecm_goods g LEFT JOIN ecm_goods_spec gs ON g.default_spec = gs.spec_id LEFT JOIN ecm_store s
								ON g.store_id = s.store_id LEFT JOIN ecm_goods_statistics gst ON g.goods_id = gst.goods_id
								WHERE g.if_show = 1 AND g.closed = 0 AND s.state = 1 AND gs.spec_id IS NOT NULL AND s.store_id IS NOT NULL ";
		if (!empty ($array[0])) { //商品分类
			$catesql = " and g.store_id in (select store_id from ecm_category_store where cate_id=$array[0])";
			$sql = $sql . $catesql;
		}
		if (!empty ($array[1])) { //品牌分类
			$brandsql = " and g.brand = (select brand_name from ecm_brand where brand_id=$array[1])";
			$sql = $sql . $brandsql;
		}
		if (!empty ($array[2])) { //价格分类
			list ($pr1, $pr2) = explode("-", $array[2]);
			if (!$pr2) {
				$pricesql = " and g.price < $pr1";
				$sql = $sql . $pricesql;
			} else {
				$pricesql = " and g.price < $pr2 and g.price > $pr1";
				$sql = $sql . $pricesql;
			}
		}
		if (!empty ($array[3])) {
			$regionsql = " and g.store_id in (select store_id from ecm_store where region_id=$array[3])";
			$sql = $sql . $regionsql;
		}
		if (!empty ($array[4])) {
			$sqlkeyword = " and g.goods_name like '%" . $array[4] . "%' ";
			$sql = $sql . $sqlkeyword;
		}
		$total = $this->db->getTotal($sql . " ) a");
		return $total;
	}

	function get_goods_daoKeywordName($array = array ()) {
		$data = array ();
		foreach ($array as $value) {
			if (!empty ($array[0])) { //商品分类
				$sql = "select cate_id,cate_name from ecm_scategory where cate_id=$array[0]";
				$data['catearray'] = $this->db->getAll($sql);
			}
			if (!empty ($array[1])) { //品牌分类
				$sql = "select brand_id,brand_name from ecm_brand where brand_id=$array[1]";
				$data['brandarray'] = $this->db->getAll($sql);
			}
			if (!empty ($array[2])) { //价格分类
				$sql = "select price_id,price_name from ecm_price where tag like '%" . $array[2] . "%'";
				$data['pricearray'] = $this->db->getAll($sql);
			}
			if (!empty ($array[3])) { //地区分类
				$sql = "select region_id,region_name from ecm_region where region_id=$array[3]";
				$data['regionarray'] = $this->db->getAll($sql);
			}
			if (!empty ($array[4])) { //关键字
				$sql = "select goods_id,goods_name from ecm_goods where goods_name like '%" . $array[4] . "%'";
				$data['keywordarray'] = $this->db->getAll($sql);
			}
		}
		return $data;
	}

	function get_store_daoKeywordName($array = array ()) {
		$data = array ();
		foreach ($array as $value) {
			if (!empty ($array[0])) { //商品分类
				$sql = "select cate_id,cate_name from ecm_scategory where cate_id=$array[0]";
				$data['catearray'] = $this->db->getAll($sql);
			}
			if (!empty ($array[1])) { //地区分类
				$sql = "select region_id,region_name from ecm_region where region_id=$array[1]";
				$data['regionarray'] = $this->db->getAll($sql);
			}
		}
		return $data;
	}

	/**
	 *  关联查找多对多关系的记录
	 *
	 *  @author Garbin
	 *  @param  mixed  $params
	 *  @return array
	 */
	function findAll($params = array ()) {
		$params = $this->_initFindParams($params);
		extract($params);
		$pri_data = $this->find($params); //先找出通过JOIN获得的数据集
		if (!empty ($include) && !empty ($pri_data)) {
			$ids = array ();
			if ($index_key != $this->prikey) {
				foreach ($pri_data as $pk => $pd) {
					$ids[] = $pd[$this->prikey];
				}
			} else {
				$ids = array_keys($pri_data);
			}

			foreach ($include as $relation_name => $find_param) {
				if (is_string($find_param)) {
					$relation_name = $find_param;
					$find_param = array ();
				}

				/* 依次获取关联数据，并将其放放主数据集中 */
				$related_data = $this->getRelatedData($relation_name, $ids, $find_param);

				is_array($related_data) && $pri_data = $this->assemble($relation_name, $related_data, $pri_data);
			}
		}

		return $pri_data;
	}

	/**
	 *    获取一对多，多对多的关联数据
	 *
	 *    @author    Garbin
	 *    @param     string $relation_name       关系名称
	 *    @param     array $ids         主表的主键值列表
	 *    @param     array $find_param  关联的条件
	 *    @return    void
	 */
	function getRelatedData($relation_name, $ids, $find_param = array ()) {
		$relation_info = $this->getRelation($relation_name);
		$model = & m($relation_info['model']);
		if (empty ($ids)) {
			$this->_error('no_ids_to_assoc', $model->getName());

			return false;
		}

		if ($relation_info['type'] != HAS_MANY && $relation_info['type'] != HAS_AND_BELONGS_TO_MANY) {
			$this->_error('invalid_assoc_model', $model->getName());

			return false;
		}

		$alias = $model->alias;
		/* 如果是多对多关系，则连接的表的别名为指定别名或中间表名，否则为模型的别名 */
		if ($relation_info['type'] == HAS_AND_BELONGS_TO_MANY) {
			$be_related = $model->getRelation($relation_info['reverse']);
			$alias = isset ($be_related['alias']) ? $be_related['alias'] : $be_related['middle_table'];
		}

		/* 构造查询条件 */
		$conditions = $alias . '.' . $relation_info['foreign_key'] . ' ' . db_create_in($ids); //主键值限定
		$conditions .= $relation_info['ext_limit'] ? ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $alias) : '';
		$conditions .= is_string($find_param['conditions']) ? ' AND ' . $find_param['conditions'] : '';
		$find_param['conditions'] = $conditions;

		/* 查询字段 */
		$find_param['fields'] = !empty ($find_param['fields']) ? $find_param['fields'] . ',' . $alias . '.' . $relation_info['foreign_key'] : '';
		switch ($relation_info['type']) {
			case HAS_MANY :
				break;
			case HAS_AND_BELONGS_TO_MANY :
				$find_param['join'] = !empty ($find_param['join']) ? $find_param['join'] . ',' . $relation_info['reverse'] : $relation_info['reverse'];
				empty ($find_param['order']) && $find_param['order'] = $model->alias . ".{$model->prikey} DESC";
				$find_param['index_key'] = array (
					$relation_info['foreign_key'],
					$model->prikey
				);
				break;
		}

		return $model->find($find_param);
	}

	/**
	 *  添加一条记录
	 *
	 *  @author Garbin
	 *  @param  array $data
	 *  @return mixed
	 */
	function add($data, $compatible = false) {
		if (empty ($data) || !$this->dataEnough($data)) {
			return false;
		}

		$data = $this->_valid($data);
		if (!$data) {
			$this->_error('no_valid_data');
			return false;
		}
		$insert_info = $this->_getInsertInfo($data);
		$mode = $compatible ? 'REPLACE' : 'INSERT';

		$this->db->query("{$mode} INTO {$this->table}{$insert_info['fields']} VALUES{$insert_info['values']}");
		$insert_id = $this->db->insert_id();
		if ($insert_id) {
			if ($insert_info['length'] > 1) {
				for ($i = $insert_id; $i < $insert_id + $insert_info['length']; $i++) {
					$id[] = $i;
				}
			} else {
				/* 添加单条记录 */
				$id = $insert_id;
			}
		}

		return $id;
	}

	/**
	 *  添加多对多关联的中间表关联数据
	 *
	 *  @author Garbin
	 *  @param  string  $relation_name
	 *  @param  int     $id
	 *  @param  mixed   $ids
	 *  @return bool
	 */
	function createRelation($relation_name, $id, $ids) {
		return $this->_relationLink('create', $relation_name, $id, $ids);
	}

	/**
	 *    更新多对多关系中的关系数据
	 *
	 *    @author    Garbin
	 *    @param     string $rela
	 *    @param     int    $id
	 *    @param     mixed  $ids
	 *    @param     mixed  $update_values
	 *    @return    bool
	 */
	function updateRelation($relation_name, $id, $ids, $update_values) {
		return $this->_relationLink('update', $relation_name, $id, $ids, $update_values);
	}

	/**
	 *    去除多对多的关联链接
	 *
	 *    @author    Garbin
	 *    @param     string   $relation_name (欲删除关系名称)
	 *    @param     mixed    $conditions    条件
	 *    @param     array    $ids 关联模型的主键值集合(被拥有者ID列表),可为空
	 *    @return    bool
	 */
	function unlinkRelation($relation_name, $conditions, $ids = null) {
		return $this->_relationLink('drop', $relation_name, $conditions, $ids);
	}

	/**
	 *    对多对多关联表操作
	 *
	 *    @author    Garbin
	 *    @param
	 *    @return    void
	 */
	function _relationLink($action, $relation_name, $id, $ids, $update_values = array ()) {
		if ((empty ($ids) && $action == 'create') || !$id || !$relation_name) {
			$this->_error('relation_link_param_error');

			return false;
		}
		$relation_info = $this->getRelation($relation_name);
		if ($relation_info['type'] !== HAS_AND_BELONGS_TO_MANY) {
			/* 若不是多对多的关系，则不支持创建关系链接 */
			$this->_error('relation_link_not_support_type');

			return false;
		}

		/* 被关联模型的反向关联信息 */
		$model = & m($relation_info['model']);
		$be_related = $model->getRelation($relation_info['reverse']);
		if ($be_related['type'] !== HAS_AND_BELONGS_TO_MANY) {
			$this->_error('be_related_link_not_support_type');

			return false;
		}

		/* 开始对链接进行操作 */
		switch ($action) {
			/* 创建链接 */
			case 'create' :
				$data = array ();

				/* 形成一个统一的array(1, 2, 3)类的数组 */
				if (is_numeric($ids)) {
					$ids = array (
						$ids
					);
				}
				elseif (is_string($ids)) {
					$ids = explode(',', $ids);
					array_unique($ids);
				}
				$ext_limit_data = is_array($relation_info['ext_limit']) ? $relation_info['ext_limit'] : array ();
				/* 对数组进行分情况处理，如果是array(1, 2, 3)类的，则认为只指定了被关联表的主键值 */
				foreach ($ids as $key => $value) {
					$related_data = array ();

					/* 本表在关联表中的外键值 */
					$related_data[$relation_info['foreign_key']] = $id;

					/* 指定了除了主键值外的其它值 */
					if (is_array($value)) {
						/* 外表在关联表中的外键值 */
						$related_data[$be_related['foreign_key']] = intval($key);

						/* 将索引数据与扩展数据合并 */
						$related_data = array_merge($related_data, $value);
					} else //仅指定了被关联表的主键值
						{
						/* 外表在关联表中的外键值 */
						$related_data[$be_related['foreign_key']] = intval($value);
					}

					/* 逐项添加 */
					$data[] = array_merge($related_data, $ext_limit_data);
				}
				$insert_info = $this->_getInsertInfo($data);

				/* 创建链接 */
				return $this->db->query("REPLACE INTO {$this->_prefix}{$relation_info['middle_table']}{$insert_info['fields']} VALUES{$insert_info['values']}");
				break;
			case 'update' :
				if (empty ($update_values)) {
					return false;
				}
				if (is_string($update_values)) {
					$update_fields = $update_values;
				}
				elseif (is_array($update_values)) {
					$update_fields = array ();
					foreach ($update_values as $_field => $_value) {
						$update_fields[] = "{$_field}='{$_value}'";
					}
					$update_fields = implode(',', $update_fields);
				} else {
					return false;
				}

				return $this->db->query("UPDATE {$this->_prefix}{$relation_info['middle_table']} SET {$update_fields} WHERE {$relation_info['foreign_key']} = {$id} AND {$be_related['foreign_key']} " . db_create_in($ids));
				break;

				/* 删除链接 */
			case 'drop' :
				/* 是数字，则认为，删除所有与$id有关联的关系*/
				if (is_numeric($id)) {
					/* 本表主键是一个值，则可以指定被关联模型的主键值 */
					$where = "{$relation_info['foreign_key']}=" . $id;

					/* 指定了被关联模型的主键值，则将该限定条件加上 */
					$where .= !empty ($ids) ? " AND {$be_related['foreign_key']} " . db_create_in($ids) : '';
				}
				elseif (is_array($id)) //是一个数组，则认为删除所有与$id列表中的主键有关系的关系
				{
					/* 如果本表的主键是一个数组，则表示要删除所有外键为指定集合的关联，在这种情况下，无法指定被关联模型的主键值 */
					$where = "{$relation_info['foreign_key']} " . db_create_in($id);
				}
				elseif (is_string($id)) //是一个字符串，则认为是自定义的条件来限制操作
				{
					$where = $id;
				}

				$where .= is_array($relation_info['ext_limit']) ? ' AND ' . $this->_getExtLimit($relation_info['ext_limit']) : '';

				return $this->db->query("DELETE FROM {$this->_prefix}{$relation_info['middle_table']} WHERE {$where}");
				break;
		}

		return true;
	}

	/**
	 *  简化更新操作
	 *
	 *  @author Garbin
	 *  @param  array   $edit_data
	 *  @param  mixed   $conditions
	 *  @return bool
	 */
	function edit($conditions, $edit_data) {
		if (empty ($edit_data)) {
			return false;
		}
		$edit_data = $this->_valid($edit_data);
		if (!$edit_data) {
			return false;
		}
		$edit_fields = $this->_getSetFields($edit_data);
		$conditions = $this->_getConditions($conditions, false);
		$this->db->query("UPDATE {$this->table} SET {$edit_fields}{$conditions}");

		return $this->db->affected_rows();

	}

	/**
	 *  简化删除记录操作
	 *
	 *  @author Garbin
	 *  @param  mixed $ids
	 *  @return int
	 */
	function drop($conditions, $fields = '') {
		if (empty ($conditions)) {
			return;
		}
		if ($conditions === DROP_CONDITION_TRUNCATE) {
			$conditions = '';
		} else {
			$conditions = $this->_getConditions($conditions, false);
		}

		/* 保存删除的记录的主键值，便于关联删除时使用 */
		$fields && $fields = ',' . $fields;

		/* 这是个瓶颈，当删除的数据量非常大时会有问题 */
		$this->_saveDroppedData("SELECT {$this->prikey}{$fields} FROM {$this->table}{$conditions}");

		$droped_data = $this->getDroppedData();
		if (empty ($droped_data)) {
			return 0;
		}

		$this->db->query("DELETE FROM {$this->table}{$conditions}");
		$affectedRows = $this->db->affected_rows();
		if ($affectedRows > 0) {
			/* 删除依赖数据 */
			$this->dropDependentData(array_keys($droped_data));
		}

		return $affectedRows;
	}

	/**
	 *  删除依赖数据
	 *
	 *  @author Garbin
	 *  @param  mixed $keys     本表的主键值集合
	 *  @return bool
	 */
	function dropDependentData($keys) {
		if (empty ($keys)) {
			$this->_error('keys_is_empty');

			return false;
		}
		if (is_numeric($keys)) {
			$keys = array (
				$keys
			);
		}
		elseif (is_string($keys)) {
			$keys = explode(',', $keys);
		}

		/* 获取所有关系 */
		$relation = $this->getRelation();
		if (empty ($relation)) {
			return true;
		}

		/* 依次将关系中的依赖数据删除 */
		foreach ($relation as $relation_name => $relation_info) {
			/* 如果是多对多关系，则只解除关联表中的数据 */
			if ($relation_info['type'] === HAS_AND_BELONGS_TO_MANY) {
				$this->unlinkRelation($relation_name, $keys);
			}
			elseif ($relation_info['dependent'] && $relation_info['type'] !== BELONGS_TO) {
				/* 如果是指定了dependent依赖性，则调用drop删除之 */
				if ($relation_info['model'] != $this->_name) {
					/* 若关联的模型不是本身，则直接使用m取得模型对象 */
					$model = & m($relation_info['model']);
				} else {
					/* 否则要创建一个新的模型对象以避免操作时互相影响 */
					$model = & m($relation_info['model'], array (), true);
				}

				/* 开始删除 */
				$ext_limit = (isset ($relation_info['ext_limit']) && $relation_info['ext_limit']) ? ' AND ' . $this->_getExtLimit($relation_info['ext_limit']) : '';

				/* 默认限定键为主键 */
				$limit_keys = $keys;

				/*当关联类型是一对一拥有的关系且设定了参考键时，说明外表的外键值不是本表的主键值，而是参考键的值，因此限定键是参考键，要找出参考键的值的集合*/
				if ($relation_info['type'] == HAS_ONE && isset ($relation_info['refer_key'])) {
					/* 找出参考键值的集合,本表的参考键值集合 */
					$limit_keys = $this->db->getCol("SELECT DISTINCT {$relation_info['refer_key']} FROM {$this->table} WHERE {$this->prikey} " . db_create_in($keys));
					if ($limit_keys === false) {
						continue;
					}
				}

				/* 外表的外键=限定键(默认为主键)的都删除 */
				$conditions = "{$relation_info['foreign_key']} " . db_create_in($limit_keys) . $ext_limit;

				/* 删除数据 */
				$model->drop($conditions);
			}
		}
	}

	/**
	 *  获取扩展限制
	 *
	 *  @author Garbin
	 *  @param  array $ext_limit
	 *  @param  string $alias
	 *  @return string
	 */
	function _getExtLimit($ext_limit, $alias = null) {
		if (!$ext_limit) {
			return;
		}
		$str = '';
		$pre = '';
		if ($alias) {
			$pre = "{$alias}.";
		}
		foreach ($ext_limit as $_k => $_v) {
			$str .= $str == '' ? " {$pre}{$_k} = '{$_v}'" : " AND {$pre}{$_k} = '{$_v}'";
		}

		return $str;
	}

	/**
	 *  获取时时保存的已删除记录
	 *
	 *  @author Garbin
	 *  @return array
	 */
	function getDroppedData() {
		return $this->_dropped_data;
	}

	/**
	 *  获取统计数
	 *
	 *  @author Garbin
	 *  @return int
	 */
	function getCount() {
		return $this->_last_query_count;
	}

	/*
	 * function get_store_count($array=array()){
	   if(!empty($array[0])){ //店铺分类
	         $catesql=" and category_store.cate_id = $array[0]";
	    }
	   if(!empty($array[1])){ //地区分类
	    	 $regionsql=" and region_id = $array[1]";
	    }
	   $sql="select count(*) from " .
	   		"(SELECT s.store_id FROM ecm_store s LEFT JOIN ecm_member member  " .
	   		"ON s.store_id = member.user_id LEFT JOIN ecm_category_store category_store  " .
	   		"ON s.store_id = category_store.store_id  WHERE state = 1) a";
	   $total=$this->db->getAll($sql);
	   return $total;
	}
	 */

	/**
	 *  临时保存已删除的记录数据
	 *
	 *  @author Garbin
	 *  @param  string $sql
	 *  @return void
	 */
	function _saveDroppedData($sql) {
		$this->_dropped_data = $this->db->getAllWithIndex($sql, $this->prikey);
	}

	/**
	 *  更新查询统计数
	 *
	 *  @author Garbin
	 *  @param  string $sql
	 *  @return void
	 */
	function _updateLastQueryCount($sql) {
		$this->_last_query_count = $this->db->getOne($sql);
	}

	/**
	 *  获取条件句段
	 *
	 *  @author Garbin
	 *  @param  mixed   $conditions
	 *  @return string
	 */
	function _getConditions($conditions, $if_add_alias = false) {
		$alias = '';
		if ($if_add_alias) {
			$alias = $this->alias . '.';
		}
		if (is_numeric($conditions)) {
			/* 如果是一个数字或数字字符串，则认为其是主键值 */
			return " WHERE {$alias}{$this->prikey} = {$conditions}";
		}
		elseif (is_string($conditions)) {
			/* 如果是字符串，则认为其是SQL自定义条件 */
			if (substr($conditions, 0, 6) == 'index:') {
				$value = substr($conditions, 6);
				return $value ? " WHERE {$alias}{$this->prikey}='{$value}'" : '';
			} else {
				return $conditions ? ' WHERE ' . $conditions : '';
			}
		}
		elseif (is_array($conditions)) {
			/* 如果是数组，则认为其是一个主键集合 */
			if (empty ($conditions)) {
				return '';
			}
			foreach ($conditions as $_k => $_v) {
				if (!$_v) {
					unset ($conditions[$_k]);
				}
			}
			$conditions = array_unique($conditions);

			return ' WHERE ' . $alias . $this->prikey . ' ' . db_create_in($conditions);
		}
		elseif (is_null($conditions)) {
			return '';
		}
	}

	/**
	 *  获取设置字段
	 *
	 *  @author Garbin
	 *  @param  array $data
	 *  @return string
	 */
	function _getSetFields($data) {
		if (!is_array($data)) {
			return $data;
		}
		$fields = array ();
		foreach ($data as $_k => $_v) {
			!is_array($_v) && $fields[] = "{$_k}='{$_v}'";
		}

		return implode(',', $fields);
	}

	/**
	 *    获取查询时的字段列表
	 *
	 *    @author    Garbin
	 *    @param     string $src_fields_list
	 *    @return    string
	 */
	function getRealFields($src_fields_list) {
		$fields = $src_fields_list;
		if (!$src_fields_list) {
			$fields = '';
		}
		$fields = preg_replace('/([a-zA-Z0-9_]+)\.([a-zA-Z0-9_*]+)/e', "\$this->_getFieldTable('\\1') . '.\\2'", $fields);

		return $fields;
	}

	/**
	 *    解析字段所属
	 *
	 *    @author    Garbin
	 *    @param     string $owner
	 *    @return    string
	 */
	function _getFieldTable($owner) {
		if ($owner == 'this') {
			return $this->alias;
		} else {
			$m = & m($owner);
			if ($m === false) {
				/* 若没有对象，则原样返回 */

				return $owner;
			}

			return $m->alias;
		}
	}

	/**
	 *  获取插入的数据SQL
	 *
	 *  @author Garbin
	 *  @param  array $data
	 *  @return string
	 */
	function _getInsertInfo($data) {
		reset($data);
		$fields = array ();
		$values = array ();
		$length = 1;
		if (key($data) === 0 && is_array($data[0])) {
			$length = count($data);
			foreach ($data as $_k => $_v) {
				foreach ($_v as $_f => $_fv) {
					$is_array = is_array($_fv);
					($_k == 0 && !$is_array) && $fields[] = $_f;
					!$is_array && $values[$_k][] = "'{$_fv}'";
				}
				$values[$_k] = '(' . implode(',', $values[$_k]) . ')';
			}
		} else {
			foreach ($data as $_k => $_v) {
				$is_array = is_array($_v);
				!$is_array && $fields[] = $_k;
				!$is_array && $values[] = "'{$_v}'";
			}
			$values = '(' . implode(',', $values) . ')';
		}
		$fields = '(' . implode(',', $fields) . ')';
		is_array($values) && $values = implode(',', $values);

		return compact('fields', 'values', 'length');
	}

	/**
	 *  验证数据合法性，当只验证vrule中指定的字段，并且只当$data中设置了其值时才验证
	 *
	 *  @author Garbin
	 *  @param  array $data
	 *  @return mixed
	 */
	function _valid($data) {
		if (empty ($this->_autov) || empty ($data) || !is_array($data)) {
			return $data;
		}
		$max = $filter = $reg = $default = $valid = '';
		reset($data);
		$is_multi = (key($data) === 0 && is_array($data[0]));
		if (!$is_multi) {
			$data = array (
				$data
			);
		}
		foreach ($this->_autov as $_k => $_v) {
			if (is_array($_v)) {
				$required = (isset ($_v['required']) && $_v['required']) ? true : false;
				$type = isset ($this->_autov[$_k]['type']) ? $this->_autov[$_k]['type'] : 'string';
				$min = isset ($this->_autov[$_k]['min']) ? $this->_autov[$_k]['min'] : 0;
				$max = isset ($this->_autov[$_k]['max']) ? $this->_autov[$_k]['max'] : 0;
				$filter = isset ($this->_autov[$_k]['filter']) ? $this->_autov[$_k]['filter'] : '';
				$valid = isset ($this->_autov[$_k]['valid']) ? $this->_autov[$_k]['valid'] : '';
				$reg = isset ($this->_autov[$_k]['reg']) ? $this->_autov[$_k]['reg'] : '';
				$default = isset ($this->_autov[$_k]['default']) ? $this->_autov[$_k]['default'] : '';
			} else {
				preg_match_all('/([a-z]+)(\((\d+),(\d+)\))?/', $_v, $result);
				$type = $result[1];
				$min = $result[3];
				$max = $result[4];
			}
			foreach ($data as $_sk => $_sd) {
				$has_set = isset ($data[$_sk][$_k]);
				if (!$has_set) {
					continue;
				}

				if ($required && $data[$_sk][$_k] == '') {
					$this->_error("required_field", $_k);

					return false;
				}

				/* 运行到此，说明该字段不是必填项可以为空 */

				$value = $data[$_sk][$_k];

				/* 默认值 */
				if (!$value && $default) {
					$data[$_sk][$_k] = function_exists($default) ? $default () : $default;
					continue;
				}

				/* 若还是空值，则没必要往下验证长度，正则，自定义和过滤，因为其已经是一个空值了 */
				if (!$value) {
					continue;
				}

				/* 大小|长度限制 */
				if ($type == 'string') {
					$strlen = strlen($value);
					if ($min != 0 && $strlen < $min) {
						$this->_error('autov_length_lt_min', $_k);

						return false;
					}
					if ($max != 0 && $strlen > $max) {
						$this->_error('autov_length_gt_max', $_k);

						return false;
					}
				} else {
					if ($min != 0 && $value < $min) {
						$this->_error('autov_value_lt_min', $_k);

						return false;
					}
					if ($max != 0 && $value > $max) {
						$this->_error('autov_value_gt_max', $_k);

						return false;
					}
				}

				/* 正则 */
				if ($reg) {
					if (!preg_match($reg, $value)) {
						$this->_error('check_match_error', $_k);
						return false;
					}
				}

				/* 自定义验证 */
				if ($valid && function_exists($valid)) {
					$result = $valid ($value);
					if ($result !== true) {
						$this->_error($result);

						return false;
					}
				}

				/* 过滤 */
				if ($filter) {
					$funs = explode(',', $filter);
					foreach ($funs as $fun) {
						function_exists($fun) && $value = $fun ($value);
					}
					$data[$_sk][$_k] = $value;
				}
			}
		}
		if (!$is_multi) {
			$data = $data[0];
		}

		return $data;
	}

	/**
	 *  初始化查询参数
	 *
	 *  @author Garbin
	 *  @param  array $params
	 *  @return array
	 */
	function _initFindParams($params) {
		$arr = array (
			'include' => array (),
			'join' => '',
			'conditions' => '',
			'order' => '',
			'fields' => '',
			'limit' => '',
			'count' => false,
			'index_key' => $this->prikey,


		);
		if (is_array($params)) {
			return array_merge($arr, $params);
		} else {
			$arr['conditions'] = $params;
			return $arr;
		}
	}

	/**
	 *  按指定的方式LEFT JOIN指定关系的表
	 *
	 *  @author Garbin
	 *  @param  string $table
	 *  @param  string $join_object
	 *  @return string
	 */
	function _joinModel(& $table, $join) {
		$result = false;
		if (empty ($join)) {
			return false;
		}

		/* 获取要关联的关系名 */
		$relation = preg_split('/,\s*/', $join);
		array_walk($relation, create_function('&$item, $key', '$item=trim($item);'));

		foreach ($relation as $_r) {
			/* 获取关系信息 */
			if (!($_mi = $this->getRelation($_r))) {
				/* 没有该关系则跳过 */
				continue;
			}

			/* 关联关系为$_mi的模型 */
			$join_string = $this->_getJoinString($_mi);
			if ($join_string) {
				/* 连接 */
				$table .= $join_string;
				$result = true;
			}
		}

		return $result;
	}
	function _getJoinString($relation_info) {
		switch ($relation_info['type']) {
			case HAS_ONE :
				$model = & m($relation_info['model']);

				/* 联合限制 */
				$ext_limit = '';
				$relation_info['ext_limit'] && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $model->alias); //须加上当前被关联表的别名，因为有可能存在多个JOIN，并且可能存在同名字段。

				/* 获取参考键，默认是本表主键(直接拥有)，否则为间接拥有 */
				$refer_key = isset ($relation_info['refer_key']) ? $relation_info['refer_key'] : $this->prikey;

				/* 本表参考键=外表外键 */
				return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}.{$refer_key}={$model->alias}.{$relation_info['foreign_key']}{$ext_limit}";
				break;
			case BELONGS_TO :
				/* 属于关系与拥有是一个反向的关系 */
				$model = & m($relation_info['model']);
				$be_related = $model->getRelation($relation_info['reverse']);
				if (empty ($be_related)) {
					/* 没有找到反向关系 */
					$this->_error('no_reverse_be_found', $relation_info['model']);

					return '';
				}
				$ext_limit = '';
				!empty ($relation_info['ext_limit']) && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $this->alias);
				/* 获取参考键，默认是外表主键 */
				$refer_key = isset ($be_related['refer_key']) ? $be_related['refer_key'] : $model->prikey;

				/* 本表外键=外表参考键 */
				return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}.{$be_related['foreign_key']} = {$model->alias}.{$refer_key}{$ext_limit}";
				break;
			case HAS_AND_BELONGS_TO_MANY :
				/* 连接中间表，本表主键=中间表外键 */
				$malias = isset ($relation_info['alias']) ? $relation_info['alias'] : $relation_info['middle_table'];
				$ext_limit = '';
				$relation_info['ext_limit'] && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $malias);
				return " LEFT JOIN {$this->_prefix}{$relation_info['middle_table']} {$malias} ON {$this->alias}.{$this->prikey} = {$malias}.{$relation_info['foreign_key']}{$ext_limit}";
				break;
		}
	}

	/**
	 *    获取关系信息
	 *
	 *    @author    Garbin
	 *    @param     string $relation_name
	 *    @return    array
	 */
	function getRelation($relation_name = null) {
		return !is_null($relation_name) ? $this->_relation[$relation_name] : $this->_relation;
	}

	/**
	 *    获取指定关系类型的关联信息
	 *
	 *    @author    Garbin
	 *    @param     int $relation
	 *    @return    array
	 */
	function getRelationByType($relation) {
		if (empty ($relation)) {
			return $this->_relation; //返回所有关系
		}
		$arr = array ();
		foreach ($this->_relation as $relation_name => $relation_info) {
			if ($relation_info['relation'] == $relation) {
				$arr[$relation_name] = $relation_info;
			}
		}

		return $arr;
	}

	/**
	 *  组合数据
	 *
	 *  @author Garbin
	 *  @param  string  $relation_name  关系名称
	 *  @param  array   $assoc_data     关联的数据
	 *  @param  array   $pri_data       主表数据
	 *  @return array
	 */
	function assemble($relation_name, $assoc_data, $pri_data) {
		if (empty ($pri_data) || empty ($assoc_data)) {
			$this->_error('assemble_data_empty');

			return $pri_data;
		}

		/* 获取关系信息 */
		$relation_info = $this->getRelation($relation_name);
		$model = & m($relation_info['model']);

		/* 循环主数据集 */
		foreach ($pri_data as $pk => $pd) {
			/* 循环从数据集 */
			foreach ($assoc_data as $ak => $ad) {
				/* 当主表的主键值与外表的的外键值相等时，将该外表的数据加入到主表数据中键为$model->alias的数组中 */
				if ($pd[$this->prikey] == $ad[$relation_info['foreign_key']]) {
					$pd[$model->alias][$ak] = $ad;
					unset ($assoc_data[$ak]); //减少循环次数
				}
			}
			$pri_data[$pk] = $pd;
		}

		return $pri_data;
	}

	/**
	 *    检查数据是否足够
	 *
	 *    @author    Garbin
	 *    @param     array $data
	 *    @return    bool[true:足够,false:不足]
	 */
	function dataEnough($data) {
		$required_fields = $this->getRequiredFields();
		if (empty ($required_fields)) {
			return true;
		}
		$is_multi = (key($data) === 0 && is_array($data[0]));
		foreach ($required_fields as $field) {
			if ($is_multi) {
				foreach ($data as $key => $value) {
					if (!isset ($value[$field])) {
						$this->_error('data_not_enough', $field);

						return false;
					}
				}
			} else {
				if (!isset ($data[$field])) {
					$this->_error('data_not_enough', $field);

					return false;
				}
			}
		}

		return true;
	}

	/**
	 *    获取必须的字段列表
	 *
	 *    @author    Garbin
	 *    @return    array
	 */
	function getRequiredFields() {
		$fields = array ();
		if (is_array($this->_autov)) {
			foreach ($this->_autov as $key => $value) {
				if (isset ($value['required']) && $value['required']) {
					$fields[] = $key;
				}
			}
		}

		return $fields;
	}

	/**
	 * 用于统计
	 */
	function getOne($sql) {
		return $this->db->getOne($sql);
	}
	function getRow($sql) {
		return $this->db->getRow($sql);
	}
	function getCol($sql) {
		return $this->db->getCol($sql);
	}
	function getAll($sql) {
		return $this->db->getAll($sql);
	}
}
?>
